//Copyright (C) 1998-2008, Sumisho Computer Systems Corp. All Rights Reserved.

//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at

//http://www.apache.org/licenses/LICENSE-2.0

//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

/**
 * @author Hitoshi Okada
 */

package com.curlap.orb.servlet;

import java.util.List;
import java.util.ArrayList;
import javax.servlet.ServletContext;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;

import com.curlap.orb.security.AccessControlAllow;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * CurlConfigFactory
 */
public class CurlConfigFactory
{
    private static CurlConfigFactory instance = null;

    private CurlConfig config = null;
    private Log log = LogFactory.getLog(CurlConfigFactory.class);

    // constructors
    protected CurlConfigFactory(ServletContext context) 
    {
        try 
        {
            if (context == null) 
                throw new Exception("ServletContext is nothing, when loading CurlConfig file.");

            SAXParser parser = (SAXParserFactory.newInstance()).newSAXParser();
            config = new CurlConfig();
            parser.parse(
                    context.getResourceAsStream("/WEB-INF/curl-config.xml"), 
                    new InnerCurlConfigHandler(config));
            log.debug("Loading CurlConfig resource [WEB-INF/curl-config.xml]");            
        }
        // SAXException, IOException, ...etc
        catch (Exception e) 
        {
            // fatal error
            log.fatal("Could not load CurlConfig resource [WEB/INF/curl-config.xml]", e);
        }
    }

    // get instance
    public static CurlConfigFactory getInstance(ServletContext context)
    {
        if (instance == null)
        {
            instance = new CurlConfigFactory(context);
        }
        return instance;
    }

    public CurlConfig getCurlConfig()
    {
        return config;
    }


    // - - inner class - -
    // DefaultHandler for SAXParser
    class InnerCurlConfigHandler extends DefaultHandler
    {
        private CurlConfig config;
        private List<AccessControlAllow> accessControlList;

        // constructors
        private InnerCurlConfigHandler(CurlConfig config) 
        {
            this.config = config;    
        }

        @Override
        public void startDocument() 
        { 
            // do nothing    
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
        {
            // Serializer
            if (qName.equals("serializer"))
            {
                try
                {
                    // NOTE: serializer should extend com.curlap.orb.io.AbstractSerializer
                    Class cls = Class.forName(attributes.getValue("class"));
                    if (cls.getSuperclass() == com.curlap.orb.io.AbstractSerializer.class)
                        config.setSerializer(cls);
                    else
                        log.warn("serializer should extend com.curlap.orb.io.AbstractSerializer");
                } 
                catch (ClassNotFoundException ignored) 
                {}
            }
            // Object management method
            else if (qName.equals("object-management"))
            {
                config.setObjectManagementMethod(attributes.getValue("method"));
            }
            // Application Context
            else if (qName.equals("application-context"))
            {
                try 
                {
                    config.setApplicationContext(
                            attributes.getValue("name"), 
                            Class.forName(attributes.getValue("class")));
                }
                catch (ClassNotFoundException e)
                {
                    // error
                    log.error("Wrong application container in WEB-INF/curl-config.xml", e);
                }
            }
            // Access Control List
            else if (qName.equals("access-control-list"))
            {
                accessControlList = new ArrayList<AccessControlAllow>();
            }
            else if (qName.equals("allow"))
            {
                if (accessControlList == null)
                {
                    // error
                    log.error("Could not parse due to wrong XML in WEB-INF/curl-config.xml");
                }
                else
                {
                    try
                    {
                        String allowClass = attributes.getValue("class");
                        String allowInterfaceName = attributes.getValue("interface");
                        Class allowInterface = (allowInterfaceName != null ? Class.forName(allowInterfaceName) : null);
                        accessControlList.add(new AccessControlAllow(allowClass, allowInterface));
                        // trace
                        log.trace("add allow access list " + allowClass + " to WEB-INF/curl-config.xml");
                    }
                    catch (ClassNotFoundException e)
                    {
                        log.warn(e.getMessage(), e);
                    }
                }
            }
        }

        @Override
        public void characters(char[] ch, int offset, int lenght)
        {
            // do nothing
        }

        @Override
        public void endElement(String uri, String localName, String qName)
        {
            if (qName.equals("access-control-list"))
            {
                config.setAccessControlList(accessControlList.toArray(new AccessControlAllow[accessControlList.size()]));
            }
        }

        @Override
        public void endDocument()
        { 
            // do nothing    
        }

        @Override
        public void warning(SAXParseException e) 
        {
            // warn
            log.warn(e.getMessage(), e);
        }

        @Override
        public void error(SAXParseException e)
        {
            // error
            log.error(e.getMessage(), e);
        }

        @Override
        public void fatalError(SAXParseException e) 
        {
            // fatal
            log.fatal(e.getMessage(), e);
        }
    }
}
